package com.tping.hash;

import org.omg.CORBA.PUBLIC_MEMBER;

import java.util.*;

/**
 * 一致性hash算法实现
 *
 * @author xiai
 * @Date 2023/1/28 12:02
 * @Version 1.0
 */
public class ConsistencyHash {

    // 物理节点
    private List<String> realNodes = new ArrayList<>();

    // 一个物理节点拥有虚拟节点的数量
    private int virtualNums;

    private Map<String, List<Integer>> realToVirtualNodes = new HashMap<>();

    // 存储每一个虚拟节点与物理节点的关系, key是虚拟节点的hash值, value值是物理节点
    private SortedMap<Integer, String> sortedMap = new TreeMap<>();

    public ConsistencyHash(int virtualNums) {
        this.virtualNums = virtualNums;
    }

    /**
     * 添加节点
     *
     * @param node
     */
    public void addServer(String node){
        // 保存物理节点
        realNodes.add(node);

        // 物理节点 与 虚拟节点建立关联关系
        int count = 0, i = 0;
        String vnode = null;

        List<Integer> virtualNodes = new ArrayList<>(this.virtualNums);
        realToVirtualNodes.put(node, virtualNodes);

        while (count < this.virtualNums) {
            i++;
            vnode = node + "--" + i;

            // 计算Hash值
            // hash冲突
            int hashValue = FNV1_32_HASH.getHash(vnode);
            if( !sortedMap.containsKey(hashValue) ){
                virtualNodes.add(hashValue);

                // 虚拟节点放到环上去
                sortedMap.put(hashValue, node);
                count++;
            }
        }
    }

    /**
     * 获取物理节点
     *
     * @param key
     * @return
     */
    public String getServer(String key){
        // 计算Hash值
        int hash = FNV1_32_HASH.getHash(key);

        // 顺时针方向找，离他最近的节点
        // 获取当前key的后面集合数据
        SortedMap<Integer, String> subMap = sortedMap.tailMap(hash);
        if( subMap.isEmpty() ){
            // 则取有序集合的第一个值
            return sortedMap.get(sortedMap.firstKey());
        }
        return subMap.get(subMap.firstKey());
    }

    public static void main(String[] args) {
        ConsistencyHash consistencyHash = new ConsistencyHash(100);
        consistencyHash.addServer("192.168.0.20");
        consistencyHash.addServer("192.168.0.21");
        consistencyHash.addServer("192.168.0.22");
        for (int i = 0; i < 10; i++) {
            System.out.println("xiaoming-" + i + "对应物理节点：" + consistencyHash.getServer("xiaoming-" + i));
        }
    }
}
